{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 实验9 基于CNN的图像识别\n",
    "1.读取非MNIST数据\n",
    "\n",
    "2.使用训练数据生成DataSet和DataLoader对象用于模型训练\n",
    "\n",
    "3.使用torch.nn定义CNN类，自定隐藏层数和卷积核数量\n",
    "\n",
    "4.用Adam的优化方式训练模型\n",
    "\n",
    "5.在验证或测试集上测试模型性能，画出学习曲线并分析结果"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.读取非MNIST数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Test set size: 10000\n",
      "Training set size: 60000\n",
      "Number of training samples: 43360\n",
      "Number of cross-validation samples: 10850\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torchvision\n",
    "import torchvision.transforms as transforms\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "label_size = 18 # Label size\n",
    "ticklabel_size = 14 # Tick label size\n",
    "    \n",
    "# Define a transform to normalize the data\n",
    "transform = transforms.Compose([\n",
    "    transforms.ToTensor()\n",
    "])\n",
    "\n",
    "# Load test data from the MNIST\n",
    "testset = torchvision.datasets.MNIST(root='./Data', train=False, download=False, transform=transform)\n",
    "print(f\"Test set size: {len(testset)}\")\n",
    "\n",
    "# Load training data from the MNIST\n",
    "trainset = torchvision.datasets.MNIST(root='./Data', train=True, download=False, transform=transform)\n",
    "print(f\"Training set size: {len(trainset)}\")\n",
    "\n",
    "# Rate of trX and cvX\n",
    "tr_cv_rate = 0.8\n",
    "\n",
    "# Create a list to store indices for each class unique()\n",
    "class_indices = [[] for _ in range(10)]  # 10 classes in MNIST\n",
    "\n",
    "# Populate class_indices\n",
    "for idx, (_, label) in enumerate(trainset):\n",
    "    class_indices[label].append(idx)\n",
    "\n",
    "# Calculate the number of samples for each class in training and validation sets\n",
    "train_size_per_class = int(tr_cv_rate * min(len(indices) for indices in class_indices))\n",
    "val_size_per_class = min(len(indices) for indices in class_indices) - train_size_per_class\n",
    "\n",
    "# Create balanced train and validation sets\n",
    "train_indices = []\n",
    "val_indices = []\n",
    "for indices in class_indices:\n",
    "    train_indices.extend(indices[:train_size_per_class])\n",
    "    val_indices.extend(indices[train_size_per_class:train_size_per_class + val_size_per_class])\n",
    "\n",
    "# Create Subset datasets\n",
    "from torch.utils.data import Subset\n",
    "trX = Subset(trainset, train_indices)\n",
    "cvX = Subset(trainset, val_indices)\n",
    "\n",
    "print(f\"Number of training samples: {len(trX)}\")\n",
    "print(f\"Number of cross-validation samples: {len(cvX)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.使用训练数据生成DataSet和DataLoader对象用于模型训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "image_channels is 1\n",
      "tensor([[0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.]])\n"
     ]
    }
   ],
   "source": [
    "batch_size = 42 # Define training batch 1，\n",
    "\n",
    "def one_hot_collate(batch):\n",
    "    data = torch.stack([item[0] for item in batch])\n",
    "    labels = torch.tensor([item[1] for item in batch])\n",
    "    one_hot_labels = torch.zeros(labels.size(0), 10)  # 10 classes in MNIST 【0，1，0，0】\n",
    "    one_hot_labels.scatter_(1, labels.unsqueeze(1), 1)\n",
    "    return data, one_hot_labels\n",
    "\n",
    "trLoader = torch.utils.data.DataLoader(trX, batch_size=batch_size, shuffle=True, num_workers=0, collate_fn=one_hot_collate)\n",
    "cvLoader = torch.utils.data.DataLoader(cvX, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=one_hot_collate)\n",
    "teLoader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=one_hot_collate)\n",
    "\n",
    "# Get a batch of training data\n",
    "dataiter = iter(trLoader)\n",
    "data, labels = next(dataiter)\n",
    "\n",
    "image_channels = data[0].numpy().shape[0]\n",
    "print(f'image_channels is {image_channels}')\n",
    "print(labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.使用torch.nn定义CNN类，自定隐藏层数和卷积核数量\n",
    "* 输入：2-D图片\n",
    "* 输出：手写字母类型的概率分布\n",
    "* 卷积层：2层, 卷积核大小：3x3\n",
    "* 隐藏层1：100个节点\n",
    "* 隐藏层2：50个节点"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CNN(\n",
      "  (conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
      "  (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
      "  (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
      "  (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
      "  (fc1): Linear(in_features=3136, out_features=100, bias=True)\n",
      "  (fc2): Linear(in_features=100, out_features=10, bias=True)\n",
      "  (softmax): Softmax(dim=1)\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import torch.optim as optim\n",
    "\n",
    "class CNN(nn.Module):\n",
    "    def __init__(self, image_channels, num_classes):\n",
    "        super(CNN, self).__init__()\n",
    "        \n",
    "        # First convolutional layer\n",
    "        self.conv1 = nn.Conv2d(in_channels=image_channels, out_channels=32, kernel_size=3, padding=1)\n",
    "        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)\n",
    "        \n",
    "        # Second convolutional layer\n",
    "        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)\n",
    "        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)\n",
    "        \n",
    "        # Fully connected layers\n",
    "        self.fc1 = nn.Linear(64 * 7 * 7, 100)  # After two 2x2 max pools, 28x28 -> 7x7\n",
    "        self.fc2 = nn.Linear(100, num_classes)  # 10 classes output\n",
    "\n",
    "        # Softmax\n",
    "        self.softmax = nn.Softmax(dim=1)\n",
    "        \n",
    "    def forward(self, x):\n",
    "        # Remove the reshape operation and directly use x\n",
    "        x = F.relu(self.conv1(x))\n",
    "        x = self.pool1(x)\n",
    "        \n",
    "        # Second conv layer\n",
    "        x = F.relu(self.conv2(x))\n",
    "        x = self.pool2(x)\n",
    "        \n",
    "        # Flatten the output for the fully connected layers\n",
    "        x = x.view(-1, 64 * 7 * 7)\n",
    "        \n",
    "        # Fully connected layers\n",
    "        x = F.relu(self.fc1(x))\n",
    "        x = self.fc2(x)\n",
    "        \n",
    "        # Softmax\n",
    "        x = self.softmax(x)\n",
    "        \n",
    "        return x\n",
    "\n",
    "# Initialize the model\n",
    "model = CNN(image_channels, 10)\n",
    "if torch.cuda.is_available():\n",
    "    model = model.cuda()\n",
    "\n",
    "# Display model architecture\n",
    "print(model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.使用Adam作为Optimizor训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch [1/50], Train Loss: 1.6934, CV Loss: 1.5867\n",
      "Epoch [2/50], Train Loss: 1.5623, CV Loss: 1.5014\n",
      "Epoch [3/50], Train Loss: 1.4848, CV Loss: 1.4888\n",
      "Epoch [4/50], Train Loss: 1.4783, CV Loss: 1.4855\n",
      "Epoch [5/50], Train Loss: 1.4762, CV Loss: 1.4795\n",
      "Epoch [6/50], Train Loss: 1.4740, CV Loss: 1.4788\n",
      "Epoch [7/50], Train Loss: 1.4720, CV Loss: 1.4794\n",
      "Epoch [8/50], Train Loss: 1.4715, CV Loss: 1.4813\n",
      "Epoch [9/50], Train Loss: 1.4708, CV Loss: 1.4785\n",
      "Epoch [10/50], Train Loss: 1.4705, CV Loss: 1.4789\n",
      "Epoch [11/50], Train Loss: 1.4690, CV Loss: 1.4768\n",
      "Epoch [12/50], Train Loss: 1.4690, CV Loss: 1.4786\n",
      "Epoch [13/50], Train Loss: 1.4686, CV Loss: 1.4771\n",
      "Epoch [14/50], Train Loss: 1.4677, CV Loss: 1.4770\n",
      "Epoch [15/50], Train Loss: 1.4666, CV Loss: 1.4768\n",
      "Epoch [16/50], Train Loss: 1.4675, CV Loss: 1.4765\n",
      "Epoch [17/50], Train Loss: 1.4668, CV Loss: 1.4797\n",
      "Epoch [18/50], Train Loss: 1.4679, CV Loss: 1.4778\n",
      "Epoch [19/50], Train Loss: 1.4674, CV Loss: 1.4772\n",
      "Epoch [20/50], Train Loss: 1.4669, CV Loss: 1.4757\n",
      "Epoch [21/50], Train Loss: 1.4663, CV Loss: 1.4767\n",
      "Epoch [22/50], Train Loss: 1.4668, CV Loss: 1.4778\n",
      "Epoch [23/50], Train Loss: 1.4660, CV Loss: 1.4767\n",
      "Epoch [24/50], Train Loss: 1.4661, CV Loss: 1.4741\n",
      "Epoch [25/50], Train Loss: 1.4657, CV Loss: 1.4755\n",
      "Epoch [26/50], Train Loss: 1.4657, CV Loss: 1.4776\n",
      "Epoch [27/50], Train Loss: 1.4659, CV Loss: 1.4784\n",
      "Epoch [28/50], Train Loss: 1.4656, CV Loss: 1.4758\n",
      "Epoch [29/50], Train Loss: 1.4655, CV Loss: 1.4750\n",
      "Epoch [30/50], Train Loss: 1.4655, CV Loss: 1.4758\n",
      "Epoch [31/50], Train Loss: 1.4651, CV Loss: 1.4738\n",
      "Epoch [32/50], Train Loss: 1.4649, CV Loss: 1.4753\n",
      "Epoch [33/50], Train Loss: 1.4659, CV Loss: 1.4763\n",
      "Epoch [34/50], Train Loss: 1.4651, CV Loss: 1.4753\n",
      "Epoch [35/50], Train Loss: 1.4649, CV Loss: 1.4750\n",
      "Epoch [36/50], Train Loss: 1.4660, CV Loss: 1.4764\n",
      "Epoch [37/50], Train Loss: 1.4649, CV Loss: 1.4759\n",
      "Epoch [38/50], Train Loss: 1.4650, CV Loss: 1.4737\n",
      "Epoch [39/50], Train Loss: 1.4655, CV Loss: 1.4754\n",
      "Epoch [40/50], Train Loss: 1.4650, CV Loss: 1.4761\n",
      "Epoch [41/50], Train Loss: 1.4650, CV Loss: 1.4750\n",
      "Epoch [42/50], Train Loss: 1.4655, CV Loss: 1.4756\n",
      "Epoch [43/50], Train Loss: 1.4653, CV Loss: 1.4769\n",
      "Epoch [44/50], Train Loss: 1.4648, CV Loss: 1.4734\n",
      "Epoch [45/50], Train Loss: 1.4661, CV Loss: 1.4781\n",
      "Epoch [46/50], Train Loss: 1.4644, CV Loss: 1.4755\n",
      "Epoch [47/50], Train Loss: 1.4647, CV Loss: 1.4752\n",
      "Epoch [48/50], Train Loss: 1.4646, CV Loss: 1.4743\n",
      "Epoch [49/50], Train Loss: 1.4644, CV Loss: 1.4762\n",
      "Epoch [50/50], Train Loss: 1.4650, CV Loss: 1.4754\n"
     ]
    }
   ],
   "source": [
    "# Define loss function and optimizer\n",
    "criterion = nn.CrossEntropyLoss() # Loss\n",
    "optimizer = torch.optim.Adam(model.parameters()) # Adam\n",
    "\n",
    "# Lists to store losses\n",
    "train_losses = []\n",
    "cv_losses = []\n",
    "\n",
    "# Number of epochs\n",
    "num_epochs = 50\n",
    "\n",
    "for epoch in range(num_epochs):\n",
    "    model.train()\n",
    "    batch_losses = []\n",
    "    \n",
    "    for batch_x, batch_y in trLoader:\n",
    "        # Forward pass\n",
    "        outputs = model(batch_x)\n",
    "        loss = criterion(outputs, batch_y)\n",
    "        \n",
    "        # Backward pass and optimize\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        batch_losses.append(loss.item())\n",
    "    \n",
    "    # Calculate average training loss for this epoch\n",
    "    avg_train_loss = sum(batch_losses) / len(batch_losses)\n",
    "    train_losses.append(avg_train_loss)\n",
    "    \n",
    "    # Evaluate on cross-validation set\n",
    "    model.eval()\n",
    "    cv_batch_losses = []\n",
    "    with torch.no_grad():\n",
    "        for cv_x, cv_y in cvLoader:\n",
    "            cv_outputs = model(cv_x)\n",
    "            cv_loss = criterion(cv_outputs, cv_y)\n",
    "            cv_batch_losses.append(cv_loss.item())\n",
    "    \n",
    "    avg_cv_loss = sum(cv_batch_losses) / len(cv_batch_losses)\n",
    "    cv_losses.append(avg_cv_loss)\n",
    "    \n",
    "    print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}, CV Loss: {avg_cv_loss:.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.在验证或测试集上测试模型性能，画出学习曲线并分析结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on training set: 99.59%\n",
      "Accuracy on cross-validation set: 98.57%\n"
     ]
    },
    {
     "data": {
      "image/png": "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ZOj555/XoEGD9P3336t169Y6//zz9dBDD+muu+7Ss88+qwEDBkg6fo+VaZqaNGmSVq9erdmzZ+vss8/WN998ozlz5mjDhg3asGGDHA6H7/j6/I6cSFFRkUaOHKmff/5Z9913n/r06aPPPvtM8+bNU3p6uj744ANJ1vDhqVOnaurUqZo7d67Cw8O1a9cuv5/9o48+qrlz5+qPf/yjhg0bppKSEv3444+VAiOAk5AJACep6dOnm1FRUX7bhg8fbkoyV69eXe1j3W63WVJSYq5du9aUZG7evNm3b86cOeaxf17T0tLM8PBwc9euXb5thYWFZkJCgvl///d/vm2ffPKJKcn85JNP/NopyXz77bf9nnP8+PFmjx49fPefffZZU5L50Ucf+R33f//3f6Ykc8GCBdW+p2OVlpaaJSUl5qhRo8wLL7zQt33Hjh2mJPO0004zS0tLfdu//PJLU5K5aNEi0zRNs6yszExJSTEHDBhgut1u33E7d+40Q0NDzbS0tGpf/+GHHzYlmcuXL69Re70/u2HDhvltP3r0qBkREWGOHz/eb/vu3btNh8NhXn755aZpmuahQ4dMSeb8+fOP+xrvvvuuKclMT0+vUZuqat8TTzzh23b48GHT4XCYV1xxRZWPqe15Nnz4cHP48OG++88//7wpyfzHP/7hd9xvfvObE54TpaWlZl5enhkVFWU+9dRTvu3vvPNOpXPUa/r06X7/rsuXLzclmY8++qjfcYsXLzYlmS+++KJvW01/R45HknnjjTced/8LL7xQ5e/RI488YkoyV6xYYZqmaT722GOmJDMrK+u4z3XBBReY/fr1O2GbAJx8GBYIAMeIj4/XOeecU2n79u3bdfnllyspKUl2u12hoaEaPny4JFUaXlaVfv36qUOHDr774eHh6t69u9+wq+MxDEMTJkzw29anTx+/x65du1YxMTGVimlcdtllJ3x+rxdeeEEDBgxQeHi4QkJCFBoaqtWrV1f5/s4//3y/4gze4XneNm3ZskX79+/X5Zdf7jdMMi0tTUOHDq1xm2pr8uTJfvc3bNigwsJCzZgxw297+/btdc4552j16tWSpISEBHXp0kV//vOf9cQTT+jrr7+uVImwX79+CgsL03XXXafXXntN27dvr/T6ZWVlKi0t9V28zzF8+HB16dLFb2jgm2++KZfL5TfkrL7nWUWffPKJYmJi9Otf/9pvu7eXrKK8vDzdeeed6tq1q0JCQhQSEqLo6Gjl5+fX+nW9vL09x/7sL7nkEkVFRfl+9l71+R2pSVuioqJ08cUX+233ts3blkGDBkmSpkyZorffflv79u2r9FyDBw/W5s2bdcMNN+jjjz9WTk5OvdsHoGUgXAHAMZKTkytty8vL09lnn63//Oc/euCBB/Tpp59q48aNeu+99ySp2qIKXq1ataq0zeFw1OixkZGRCg8Pr/TYoqIi3/3Dhw8rMTGx0mOr2laVJ554Qr/97W91xhlnaMmSJfriiy+0ceNGjR07tso2Hvt+vMO7vMcePnxYkpSUlFTpsVVtO5b3Q/aOHTtq1H6vY//9vO2o6t81JSXFt98wDK1evVpjxozRo48+qgEDBqhNmzb63e9+p9zcXEnW8LdVq1apbdu2uvHGG9WlSxd16dLFb67NqFGjFBoa6rt4g5NhGLrmmmv07bffatOmTZKsIYGdOnXSyJEjJTXMeXbse6/q37+qn//ll1+uZ555Rtdee60+/vhjffnll9q4caPatGlT69et+PohISF+wyAl62eRlJTk+9l71ed3pCZtSUpKqjQfsm3btgoJCfG1ZdiwYVq2bJlKS0t11VVXKTU1VaeeeqoWLVrke8zs2bP12GOP6YsvvtC4cePUqlUrjRo1yvfvCuDkxZwrADhGVWtUrVmzRvv379enn37q60WQ1KzmWLRq1Upffvllpe1VFS+oyt/+9jeNGDFCzz//vN92b7CoS3uO9/o1adPIkSMVGhqqZcuW6frrr6/x6x777+dtR0ZGRqVj9+/fr9atW/vup6Wl6ZVXXpEk/fTTT3r77bc1d+5cFRcX64UXXpAknX322Tr77LNVVlamTZs26S9/+YtuvfVWJSYm6tJLL9Vf//pXv59ZxeefMWOG7r33Xr366qsKDQ3V119/rfvvv9/X5oY+z2p6TmRnZ+tf//qX5syZ47cel8vl8s3Fq+vrl5aWVppnZpqmMjMzfb1ETaFVq1b6z3/+I9M0/c6RAwcOqLS01O/faeLEiZo4caJcLpe++OILzZs3T5dffrk6duyoIUOGKCQkRLNmzdKsWbOUlZWlVatW6a677tKYMWO0Z8+eWlUXBdCy0HMFADXg/TBWcfK9JP31r38NRHOqNHz4cOXm5uqjjz7y2/7WW2/V6PGGYVR6f998802l9cFqqkePHkpOTtaiRYtkmqZv+65du7R+/foTPj4pKcnXi3K8CoM///yzvvnmm2qfZ8iQIYqIiNDf/vY3v+179+7VmjVrNGrUqCof1717d/3xj3/Uaaedpq+++qrSfrvdrjPOOEPPPvusJPmO6dGjhwYOHOi7VKyel5KSorFjx2rRokV69tlnZbPZNH36dN/+hj7PRo4cqdzcXL3//vt+2//+97/73TcMQ6ZpVnrdl19+WWVlZX7bju2hrI73Z3vsz37JkiXKz88/7s++MYwaNUp5eXlatmyZ33bvuVVVWxwOh4YPH65HHnlEkvT1119XOiYuLk4XX3yxbrzxRh05cqTKxZUBnDzouQKAGhg6dKji4+N1/fXXa86cOQoNDdWbb76pzZs3B7ppPtOnT9eTTz6pK6+8Ug888IC6du2qjz76SB9//LEknbA63wUXXKD7779fc+bM0fDhw7Vlyxb96U9/UqdOnVRaWlrr9thsNt1///269tprdeGFF+o3v/mNsrKyNHfu3BoNC5SsoYrbt2/XjBkz9PHHH+vCCy9UYmKiDh06pJUrV2rBggV66623fPO9qhIXF6d77rlHd911l6666ipddtllOnz4sO677z6Fh4drzpw5kqwgedNNN+mSSy5Rt27dFBYWpjVr1uibb77x9ea88MILWrNmjc4//3x16NBBRUVFvlLho0ePrtF7mjlzpj744ANfmfn27dv79jX0eXbVVVfpySef1FVXXaUHH3xQ3bp104cffug7J7xiY2M1bNgw/fnPf1br1q3VsWNHrV27Vq+88ori4uL8jj311FMlSS+++KJiYmIUHh6uTp06VTmk79xzz9WYMWN05513KicnR2eddZavWmD//v01bdq0Or2v4/n555/17rvvVtreq1cvXXXVVXr22Wc1ffp07dy5U6eddpo+//xzPfTQQxo/frzv3+/ee+/V3r17NWrUKKWmpiorK0tPPfWU39y3CRMm6NRTT9XAgQPVpk0b7dq1S/Pnz1daWpqv8iSAk1Rg62kAQOAcr1pg7969qzx+/fr15pAhQ8zIyEizTZs25rXXXmt+9dVXlaquHa9a4Pnnn1/pOY+t7na8aoHHtvN4r7N7927zoosuMqOjo82YmBhz8uTJ5ocfflhlxbhjuVwu8/bbbzfbtWtnhoeHmwMGDDCXLVtWqQKct1rgn//850rPIcmcM2eO37aXX37Z7NatmxkWFmZ2797dfPXVVys9Z3VKS0vN1157zTznnHPMhIQEMyQkxGzTpo05btw48+9//7tZVlZmmmb5z+6dd96p8nlefvlls0+fPmZYWJjpdDrNiRMnmt99951v/y+//GLOmDHD7NmzpxkVFWVGR0ebffr0MZ988klfVcQNGzaYF154oZmWlmY6HA6zVatW5vDhw83333+/Ru/FNE2zuLjYTExMrLJynWnW7zw79nwyTdPcu3evOXnyZL9zYv369ZWez3tcfHy8GRMTY44dO9b83//+Z6alpZnTp0/3e8758+ebnTp1Mu12u9/zVPXvWlhYaN55551mWlqaGRoaaiYnJ5u//e1vzaNHj/odV9PfkeORdNyL95w8fPiwef3115vJyclmSEiImZaWZs6ePdssKiryPc+//vUvc9y4cWa7du3MsLAws23btub48ePNzz77zHfM448/bg4dOtRs3bq1GRYWZnbo0MGcOXOmuXPnzhO2E0DLZphmhbEaAIAW56GHHtIf//hH7d69W6mpqYFuDgAALRbDAgGgBXnmmWckST179lRJSYnWrFmjp59+WldeeSXBCgCARka4AoAWJDIyUk8++aR27twpl8ulDh066M4779Qf//jHQDcNAIAWj2GBAAAAANAAKMUOAAAAAA2AcAUAAAAADYBwBQAAAAANgIIWVXC73dq/f79iYmJkGEagmwMAAAAgQEzTVG5urlJSUmSzVd83Rbiqwv79+9W+fftANwMAAABAM7Fnz54TLmtCuKpCTEyMJOsHGBsbG+DWAAAAAAiUnJwctW/f3pcRqkO4qoJ3KGBsbCzhCgAAAECNpgsFtKDFunXrNGHCBKWkpMgwDC1btqza42fMmCHDMCpdevfu7XfckiVL1KtXLzkcDvXq1UtLly5txHcBAAAAAAEOV/n5+erbt6+eeeaZGh3/1FNPKSMjw3fZs2ePEhISdMkll/iO2bBhg6ZOnapp06Zp8+bNmjZtmqZMmaL//Oc/jfU2AAAAAECGaZpmoBshWd1sS5cu1aRJk2r8mGXLlumiiy7Sjh07lJaWJkmaOnWqcnJy9NFHH/mOGzt2rOLj47Vo0aIaPW9OTo6cTqeys7MZFggAAACcxGqTDYJ6ztUrr7yi0aNH+4KVZPVc/b//9//8jhszZozmz59/3OdxuVxyuVy++zk5OQ3eVgAAgObKNE2VlpaqrKws0E0BAiI0NFR2u73ezxO04SojI0MfffSR/v73v/ttz8zMVGJiot+2xMREZWZmHve55s2bp/vuu69R2gkAANCcFRcXKyMjQwUFBYFuChAwhmEoNTVV0dHR9XqeoA1XCxcuVFxcXJXDCI+t5GGaZrXVPWbPnq1Zs2b57nvLLQIAALRkbrdbO3bskN1uV0pKisLCwmpUEQ1oSUzT1MGDB7V3715169atXj1YQRmuTNPUq6++qmnTpiksLMxvX1JSUqVeqgMHDlTqzarI4XDI4XA0SlsBAACaq+LiYrndbrVv316RkZGBbg4QMG3atNHOnTtVUlJSr3AV0GqBdbV27Vpt27ZNM2fOrLRvyJAhWrlypd+2FStWaOjQoU3VPAAAgKBiswXlR0KgwTRUj21Ae67y8vK0bds23/0dO3YoPT1dCQkJ6tChg2bPnq19+/bp9ddf93vcK6+8ojPOOEOnnnpqpee85ZZbNGzYMD3yyCOaOHGi/vGPf2jVqlX6/PPPG/39AAAAADh5BfRrik2bNql///7q37+/JGnWrFnq37+/7r33XklW0Yrdu3f7PSY7O1tLliypstdKkoYOHaq33npLCxYsUJ8+fbRw4UItXrxYZ5xxRuO+GQAAAAAntWazzlVzwjpXAADgZFBUVKQdO3aoU6dOCg8PD3RzAmrEiBHq169ftcv3VLRz50516tRJX3/9tfr169eobUPjq+53oTbZgAG2AAAACBqGYVR7mTFjRp2e97333tP9999f4+Pbt2+vjIyMKqepNKSdO3fKMAylp6c36uugYQRltUAAAACcnDIyMny3Fy9erHvvvVdbtmzxbYuIiPA7vqSkRKGhoSd83oSEhFq1w263KykpqVaPQctHz1Uz99K67Rrz5Dq9/Nn2QDcFAAC0cKZpqqC4NCCXms5USUpK8l2cTqcMw/DdLyoqUlxcnN5++22NGDFC4eHh+tvf/qbDhw/rsssuU2pqqiIjI3Xaaadp0aJFfs87YsQI3Xrrrb77HTt21EMPPaRrrrlGMTEx6tChg1588UXf/mN7lD799FMZhqHVq1dr4MCBioyM1NChQ/2CnyQ98MADatu2rWJiYnTttdfqD3/4Q72GFbpcLv3ud79T27ZtFR4erl/96lfauHGjb//Ro0d1xRVXqE2bNoqIiFC3bt20YMECSVYp/ptuuknJyckKDw9Xx44dNW/evDq3BfRcNXtHC4q15Zdc7T1aGOimAACAFq6wpEy97v04IK/9/Z/GKDKsYT6a3nnnnXr88ce1YMECORwOFRUV6fTTT9edd96p2NhYffDBB5o2bZo6d+5cbdGzxx9/XPfff7/uuusuvfvuu/rtb3+rYcOGqWfPnsd9zN13363HH39cbdq00fXXX69rrrlG//73vyVJb775ph588EE999xzOuuss/TWW2/p8ccfV6dOner8Xn//+99ryZIleu2115SWlqZHH31UY8aM0bZt25SQkKB77rlH33//vT766CO1bt1a27ZtU2Gh9bny6aef1vvvv6+3335bHTp00J49e7Rnz546twWEq2YvPtJaJDmroDjALQEAAAgOt956qy666CK/bbfffrvv9s0336zly5frnXfeqTZcjR8/XjfccIMkK7A9+eST+vTTT6sNVw8++KCGDx8uSfrDH/6g888/X0VFRQoPD9df/vIXzZw5U1dffbUk6d5779WKFSuUl5dXp/eZn5+v559/XgsXLtS4ceMkSS+99JJWrlypV155RXfccYd2796t/v37a+DAgZKsHjmv3bt3q1u3bvrVr34lwzCUlpZWp3agHOGqmXNGWmOEswpLAtwSAADQ0kWE2vX9n8YE7LUbijdIeJWVlenhhx/W4sWLtW/fPrlcLrlcLkVFRVX7PH369PHd9g4/PHDgQI0fk5ycLEk6cOCAOnTooC1btvjCmtfgwYO1Zs2aGr2vY/38888qKSnRWWed5dsWGhqqwYMH64cffpAk/fa3v9XkyZP11Vdf6bzzztOkSZM0dOhQSdKMGTN07rnnqkePHho7dqwuuOACnXfeeXVqCyyEq2YuLsITrgoIVwAAoHEZhtFgQ/MC6djQ9Pjjj+vJJ5/U/PnzddpppykqKkq33nqriourHxl0bCEMwzDkdrtr/BjDMCTJ7zHebV71WRXJ+9iqntO7bdy4cdq1a5c++OADrVq1SqNGjdKNN96oxx57TAMGDNCOHTv00UcfadWqVZoyZYpGjx6td999t85tOtlR0KKZi49iWCAAAEB9fPbZZ5o4caKuvPJK9e3bV507d9bWrVubvB09evTQl19+6bdt06ZNdX6+rl27KiwsTJ9//rlvW0lJiTZt2qRTTjnFt61NmzaaMWOG/va3v2n+/Pl+hTliY2M1depUvfTSS1q8eLGWLFmiI0eO1LlNJ7vg/2qihfP1XDEsEAAAoE66du2qJUuWaP369YqPj9cTTzyhzMxMvwDSFG6++Wb95je/0cCBAzV06FAtXrxY33zzjTp37nzCxx5bdVCSevXqpd/+9re64447lJCQoA4dOujRRx9VQUGBZs6cKcma13X66aerd+/ecrlc+te//uV7308++aSSk5PVr18/2Ww2vfPOO0pKSlJcXFyDvu+TCeGqmfPOucouLJHbbcpmM07wCAAAAFR0zz33aMeOHRozZowiIyN13XXXadKkScrOzm7SdlxxxRXavn27br/9dhUVFWnKlCmaMWNGpd6sqlx66aWVtu3YsUMPP/yw3G63pk2bptzcXA0cOFAff/yx4uPjJUlhYWGaPXu2du7cqYiICJ199tl66623JEnR0dF65JFHtHXrVtntdg0aNEgffvihbDYGt9WVYdZnoGcLlZOTI6fTqezsbMXGxga0LcWlbnX/40eSpPR7z1Wcp3ogAABAfRUVFWnHjh3q1KmTwsPDA92ck9K5556rpKQkvfHGG4Fuykmtut+F2mQDeq6aubAQm6LC7MovLlNWQQnhCgAAIEgVFBTohRde0JgxY2S327Vo0SKtWrVKK1euDHTT0EDo8wsC3kB1lKIWAAAAQcswDH344Yc6++yzdfrpp+uf//ynlixZotGjRwe6aWgg9FwFgbjIUO3LKqSoBQAAQBCLiIjQqlWrAt0MNCJ6roJAnLeoBWtdAQAAAM0W4SoIMCwQAAAAaP4IV0HAt9YVPVcAAABAs0W4CgLeYYFZ9FwBAAAAzRbhKgjEe4YFUtACAAAAaL4IV0HAybBAAAAAoNkjXAUBX88VwwIBAABOKiNGjNCtt97qu9+xY0fNnz+/2scYhqFly5bV+7Ub6nlOJoSrIOCbc8WwQAAAAElSZmambr75ZnXu3FkOh0Pt27fXhAkTtHr16kA3TZI0YcKE4y4OvGHDBhmGoa+++qrWz7tx40Zdd9119W2en7lz56pfv36VtmdkZGjcuHEN+lrHWrhwoeLi4hr1NZoSiwgHgfKCFoQrAACAnTt36qyzzlJcXJweffRR9enTRyUlJfr4449144036scff6zycSUlJQoNDW2SNs6cOVMXXXSRdu3apbS0NL99r776qvr166cBAwbU+nnbtGnTUE08oaSkpCZ7rZaCnqsg4F3nKqeoRGVuM8CtAQAALZZpSsX5gbmYNf+Mc8MNN8gwDH355Ze6+OKL1b17d/Xu3VuzZs3SF1984TvOMAy98MILmjhxoqKiovTAAw9Ikp5//nl16dJFYWFh6tGjh9544w2/5587d646dOggh8OhlJQU/e53v/Pte+6559StWzeFh4crMTFRF198cZVtvOCCC9S2bVstXLjQb3tBQYEWL16smTNn6vDhw7rsssuUmpqqyMhInXbaaVq0aFG17/3YYYFbt27VsGHDFB4erl69emnlypWVHnPnnXeqe/fuioyMVOfOnXXPPfeopMT60n7hwoW67777tHnzZhmGIcMwfG0+dljgt99+q3POOUcRERFq1aqVrrvuOuXl5fn2z5gxQ5MmTdJjjz2m5ORktWrVSjfeeKPvtepi9+7dmjhxoqKjoxUbG6spU6bol19+8e3fvHmzRo4cqZiYGMXGxur000/Xpk2bJEm7du3ShAkTFB8fr6ioKPXu3VsffvhhndtSE/RcBQFvQQvTlHIKSxQfFRbgFgEAgBappEB6KCUwr33Xfiks6oSHHTlyRMuXL9eDDz6oqKjKxx87xGzOnDmaN2+ennzySdntdi1dulS33HKL5s+fr9GjR+tf//qXrr76aqWmpmrkyJF699139eSTT+qtt95S7969lZmZqc2bN0uSNm3apN/97nd64403NHToUB05ckSfffZZle0MCQnRVVddpYULF+ree++VYRiSpHfeeUfFxcW64oorVFBQoNNPP1133nmnYmNj9cEHH2jatGnq3LmzzjjjjBP+LNxuty666CK1bt1aX3zxhXJycvzmZ3nFxMRo4cKFSklJ0bfffqvf/OY3iomJ0e9//3tNnTpV//vf/7R8+XKtWrVKkuR0Ois9R0FBgcaOHaszzzxTGzdu1IEDB3Tttdfqpptu8guQn3zyiZKTk/XJJ59o27Ztmjp1qvr166ff/OY3J3w/xzJNU5MmTVJUVJTWrl2r0tJS3XDDDZo6dao+/fRTSdIVV1yh/v376/nnn5fdbld6erqvd/LGG29UcXGx1q1bp6ioKH3//feKjo6udTtqg3AVBELtNkU7QpTnKtXRgmLCFQAAOGlt27ZNpmmqZ8+eNTr+8ssv1zXXXON3f8aMGbrhhhskydfb9dhjj2nkyJHavXu3kpKSNHr0aIWGhqpDhw4aPHiwJKsXJSoqShdccIFiYmKUlpam/v37H/e1r7nmGv35z3/Wp59+qpEjR0qyhgRedNFFio+PV3x8vG6//Xbf8TfffLOWL1+ud955p0bhatWqVfrhhx+0c+dOpaamSpIeeuihSvOk/vjHP/pud+zYUbfddpsWL16s3//+94qIiFB0dLRCQkKqHQb45ptvqrCwUK+//rov1D7zzDOaMGGCHnnkESUmJkqS4uPj9cwzz8hut6tnz546//zztXr16jqFq1WrVumbb77Rjh071L59e0nSG2+8od69e2vjxo0aNGiQdu/erTvuuMN3PnTr1s33+N27d2vy5Mk67bTTJEmdO3eudRtqi3AVJOIiQ5XnKqWoBQAAaDyhkVYPUqBeuwZMz/BBb0/QiQwcONDv/g8//FCpIMRZZ52lp556SpJ0ySWXaP78+ercubPGjh2r8ePHa8KECQoJCdG5556rtLQ0376xY8fqwgsvVGRkpN5880393//9n+85P/roI5199tkaOnSoXn31VY0cOVI///yzPvvsM61YsUKSVFZWpocffliLFy/Wvn375HK55HK5quyRq8oPP/ygDh06+IKVJA0ZMqTSce+++67mz5+vbdu2KS8vT6WlpYqNja3Ra1R8rb59+/q17ayzzpLb7daWLVt84ap3796y2+2+Y5KTk/Xtt9/W6rUqvmb79u19wUqSevXqpbi4OP3www8aNGiQZs2apWuvvVZvvPGGRo8erUsuuURdunSRJP3ud7/Tb3/7W61YsUKjR4/W5MmT1adPnzq1paaYcxUkvEUtsilqAQAAGothWEPzAnGpYVjq1q2bDMPQDz/8UKPjqwoqxwYz0zR929q3b68tW7bo2WefVUREhG644QYNGzZMJSUliomJ0VdffaVFixYpOTlZ9957r/r27ausrCz9+te/Vnp6uu/iDXUzZ87UkiVLlJOTowULFigtLU2jRo2SJD3++ON68skn9fvf/15r1qxRenq6xowZo+Limi2/Y1YxT+3Y9/bFF1/o0ksv1bhx4/Svf/1LX3/9te6+++4av0ZVP6PqXvPYgiGGYcjtdtfqtU70mhW3z507V999953OP/98rVmzRr169dLSpUslSddee622b9+uadOm6dtvv9XAgQP1l7/8pU5tqSnCVZDwrnV1lLWuAADASSwhIUFjxozRs88+q/z8/Er7s7Kyqn38Kaecos8//9xv2/r163XKKaf47kdEROjXv/61nn76aX366afasGGDr/clJCREo0eP1qOPPqpvvvlGO3fu1Jo1axQTE6OuXbv6LhEREZKkKVOmyG636+9//7tee+01XX311b5g8Nlnn2nixIm68sor1bdvX3Xu3Flbt26t8c+iV69e2r17t/bvL+9t3LBhg98x//73v5WWlqa7775bAwcOVLdu3bRr1y6/Y8LCwlRWVnbC10pPT/f7mf/73/+WzWZT9+7da9zm2vC+vz179vi2ff/998rOzvb79+revbv+3//7f1qxYoUuuugiLViwwLevffv2uv766/Xee+/ptttu00svvdQobfViWGCQ8Ba1oBw7AAA42T333HMaOnSoBg8erD/96U/q06ePSktLtXLlSj3//PPV9mrdcccdmjJligYMGKBRo0bpn//8p9577z1fMYeFCxeqrKxMZ5xxhiIjI/XGG28oIiJCaWlp+te//qXt27dr2LBhio+P14cffii3260ePXoc9/Wio6M1depU3XXXXcrOztaMGTN8+7p27aolS5Zo/fr1io+P1xNPPKHMzEy/4FCd0aNHq0ePHrrqqqv0+OOPKycnR3fffbffMV27dtXu3bv11ltvadCgQfrggw98PTteHTt21I4dO5Senq7U1FTFxMTI4XD4HXPFFVdozpw5mj59uubOnauDBw/q5ptv1rRp03xDAuuqrKxM6enpftvCwsI0evRo9enTR1dccYXmz5/vK2gxfPhwDRw4UIWFhbrjjjt08cUXq1OnTtq7d682btyoyZMnS5JuvfVWjRs3Tt27d9fRo0e1Zs2aGv9s64qeqyDBQsIAAACWTp066auvvtLIkSN122236dRTT9W5556r1atX6/nnn6/2sZMmTdJTTz2lP//5z+rdu7f++te/asGCBRoxYoQkq9rgSy+9pLPOOkt9+vTR6tWr9c9//lOtWrVSXFyc3nvvPZ1zzjk65ZRT9MILL2jRokXq3bt3ta85c+ZMHT16VKNHj1aHDh182++55x4NGDBAY8aM0YgRI5SUlKRJkybV+Odgs9m0dOlSuVwuDR48WNdee60efPBBv2MmTpyo//f//p9uuukm9evXT+vXr9c999zjd8zkyZM1duxYjRw5Um3atKmyHHxkZKQ+/vhjHTlyRIMGDdLFF1+sUaNG6Zlnnqlxe48nLy9P/fv397uMHz/eVwo+Pj5ew4YN0+jRo9W5c2ctXrxYkmS323X48GFdddVV6t69u6ZMmaJx48bpvvvuk2SFthtvvFGnnHKKxo4dqx49eui5556rd3urY5hVDdY8yeXk5MjpdCo7O7vWk/0ay+Mrtugva7bpqiFp+tPEUwPdHAAA0AIUFRVpx44d6tSpk8LDwwPdHCBgqvtdqE02oOcqSDAsEAAAAGjeCFdBIo6CFgAAAECzRrgKEvHeUuzMuQIAAACaJcJVkPAVtGBYIAAAANAsEa6CBMMCAQBAY6G+GU52DfU7QLgKEnGegha5RaUqLavbKtcAAAAVhYZany8KCgoC3BIgsIqLrQ4Mu91er+dhEeEg4a0WKEk5RaVKiAoLYGsAAEBLYLfbFRcXpwMHDkiy1jIyDCPArQKaltvt1sGDBxUZGamQkPrFI8JVkAix2xQTHqLcolIdLSgmXAEAgAaRlJQkSb6ABZyMbDabOnToUO8vFwhXQSQuMlS5RaUUtQAAAA3GMAwlJyerbdu2KinhMwZOTmFhYbLZ6j9jinAVROIiwrRHhcqiqAUAAGhgdru93vNNgJMdBS2CCOXYAQAAgOaLcBVEvOXYs1hIGAAAAGh2CFdBJN7Xc8WwQAAAAKC5IVwFEe9aVwwLBAAAAJofwlUQcTIsEAAAAGi2CFdBhGGBAAAAQPNFuAoiVAsEAAAAmi/CVRBxRljDAo/ScwUAAAA0O4SrIOIdFphNzxUAAADQ7BCugoh3natcV6lKytwBbg0AAACAighXQcTpKcUuSdlUDAQAAACaFcJVELHbDMWGh0iiqAUAAADQ3BCugox3aGB2IUUtAAAAgOaEcBVkvEUtjubTcwUAAAA0J4SrIOP09FxlMecKAAAAaFYIV0EmLsK7kDDDAgEAAIDmhHAVZLzDAiloAQAAADQvhKsgUz4skJ4rAAAAoDkhXAUZX0ELeq4AAACAZoVwFWTiPOEqm3AFAAAANCuEqyATF8GwQAAAAKA5IlwFmTjWuQIAAACaJcJVkInzFLTIZp0rAAAAoFkhXAUZ7zpXea5SFZe6A9waAAAAAF4BDVfr1q3ThAkTlJKSIsMwtGzZshM+xuVy6e6771ZaWpocDoe6dOmiV1991bd/4cKFMgyj0qWoqKgR30nTiY0IlWFYt+m9AgAAAJqPkEC+eH5+vvr27aurr75akydPrtFjpkyZol9++UWvvPKKunbtqgMHDqi0tNTvmNjYWG3ZssVvW3h4eIO1O5DsNkOx4aHKLixRdmGx2sQ4At0kAAAAAApwuBo3bpzGjRtX4+OXL1+utWvXavv27UpISJAkdezYsdJxhmEoKSmpoZrZ7MRHWuGKta4AAACA5iOo5ly9//77GjhwoB599FG1a9dO3bt31+23367CwkK/4/Ly8pSWlqbU1FRdcMEF+vrrr6t9XpfLpZycHL9Lc+b0FLXIIlwBAAAAzUZAe65qa/v27fr8888VHh6upUuX6tChQ7rhhht05MgR37yrnj17auHChTrttNOUk5Ojp556SmeddZY2b96sbt26Vfm88+bN03333deUb6VevEUtsgpY6woAAABoLoKq58rtdsswDL355psaPHiwxo8fryeeeEILFy709V6deeaZuvLKK9W3b1+dffbZevvtt9W9e3f95S9/Oe7zzp49W9nZ2b7Lnj17muot1Ul8pDdc0XMFAAAANBdB1XOVnJysdu3ayel0+radcsopMk1Te/furbJnymazadCgQdq6detxn9fhcMjhCJ7CEN61rrIK6bkCAAAAmoug6rk666yztH//fuXl5fm2/fTTT7LZbEpNTa3yMaZpKj09XcnJyU3VzEbn9AwLpKAFAAAA0HwENFzl5eUpPT1d6enpkqQdO3YoPT1du3fvlmQN17vqqqt8x19++eVq1aqVrr76an3//fdat26d7rjjDl1zzTWKiIiQJN133336+OOPtX37dqWnp2vmzJlKT0/X9ddf3+Tvr7F4hwVmE64AAACAZiOgwwI3bdqkkSNH+u7PmjVLkjR9+nQtXLhQGRkZvqAlSdHR0Vq5cqVuvvlmDRw4UK1atdKUKVP0wAMP+I7JysrSddddp8zMTDmdTvXv31/r1q3T4MGDm+6NNTKGBQIAAADNj2GaphnoRjQ3OTk5cjqdys7OVmxsbKCbU8mnWw5oxoKN6pUcqw9vOTvQzQEAAABarNpkg6CacwWLt+cqu5BhgQAAAEBzQbgKQqxzBQAAADQ/hKsgFO/pucovLlNxqTvArQEAAAAgEa6CUkx4iAzDuk1RCwAAAKB5IFwFIZvN8K11lUU5dgAAAKBZIFwFKe/QQMIVAAAA0DwQroKUk6IWAAAAQLNCuApS8ZEMCwQAAACaE8JVkPKudUVBCwAAAKB5IFwFKQpaAAAAAM0L4SpIeQtaHCVcAQAAAM0C4SpIxXnmXGUzLBAAAABoFghXQcobro7m03MFAAAANAeEqyBVXtCCcAUAAAA0B4SrIBXnKWiRzTpXAAAAQLNAuApSFLQAAAAAmhfCVZByeuZcFZaUqaikLMCtAQAAAEC4ClIxjhDZDOt2DvOuAAAAgIAjXAUpm83wFbVgaCAAAAAQeISrIOYtapFFUQsAAAAg4AhXQcw774qeKwAAACDwCFdBzFsxMLuQnisAAAAg0AhXQax8WCA9VwAAAECgEa6CGAUtAAAAgOaDcBXE4jxzrhgWCAAAAAQe4SqIecMVwwIBAACAwCNcBbHyYYH0XAEAAACBRrgKYhS0AAAAAJoPwlUQ85ZiJ1wBAAAAgUe4CmK+OVcUtAAAAAACjnAVxJyecFVU4lZRSVmAWwMAAACc3AhXQSzGESK7zZDE0EAAAAAg0AhXQcwwjPKiFgwNBAAAAAKKcBXknKx1BQAAADQLhKsgV14xkJ4rAAAAIJAIV0GOta4AAACA5oFwFeTiPD1XRwlXAAAAQEARroIca10BAAAAzQPhKsh5hwVm03MFAAAABBThKsjFRXmHBdJzBQAAAAQS4SrIUdACAAAAaB4IV0EujnWuAAAAgGaBcBXkfOtcUdACAAAACCjCVZBzMiwQAAAAaBYIV0Eu3lPQwlXqVmFxWYBbAwAAAJy8CFdBLirMrhCbIYmhgQAAAEAgEa6CnGEYFLUAAAAAmgHCVQsQF8laVwAAAECgEa5aAO9aV9n0XAEAAAABQ7hqAbzDAo8SrgAAAICAIVy1AHGsdQUAAAAEHOGqBWBYIAAAABB4hKsWwLvWFQUtAAAAgMAhXLUAzghKsQMAAACBRrhqAXzrXBUSrgAAAIBAIVy1APHeghYMCwQAAAAChnDVAjAsEAAAAAg8wlUL4BsWWFAi0zQD3BoAAADg5ES4agG8wwKLy9wqLCkLcGsAAACAkxPhqgWIDLMr1G5IYmggAAAAECiEqxbAMAzFRbLWFQAAABBIhKsWIs5T1CKbnisAAAAgIAhXLQRrXQEAAACBRbhqIRgWCAAAAAQW4aqFiGOtKwAAACCgAhqu1q1bpwkTJiglJUWGYWjZsmUnfIzL5dLdd9+ttLQ0ORwOdenSRa+++qrfMUuWLFGvXr3kcDjUq1cvLV26tJHeQfNRvtYVPVcAAABAIAQ0XOXn56tv37565plnavyYKVOmaPXq1XrllVe0ZcsWLVq0SD179vTt37Bhg6ZOnapp06Zp8+bNmjZtmqZMmaL//Oc/jfEWmg3vsEB6rgAAAIDACAnki48bN07jxo2r8fHLly/X2rVrtX37diUkJEiSOnbs6HfM/Pnzde6552r27NmSpNmzZ2vt2rWaP3++Fi1a1GBtb24oaAEAAAAEVlDNuXr//fc1cOBAPfroo2rXrp26d++u22+/XYWFhb5jNmzYoPPOO8/vcWPGjNH69euP+7wul0s5OTl+l2AT7+u5YlggAAAAEAgB7bmqre3bt+vzzz9XeHi4li5dqkOHDumGG27QkSNHfPOuMjMzlZiY6Pe4xMREZWZmHvd5582bp/vuu69R297YKGgBAAAABFZQ9Vy53W4ZhqE333xTgwcP1vjx4/XEE09o4cKFfr1XhmH4Pc40zUrbKpo9e7ays7N9lz179jTae2gsToYFAgAAAAEVVD1XycnJateunZxOp2/bKaecItM0tXfvXnXr1k1JSUmVeqkOHDhQqTerIofDIYfD0WjtbgoVhwWeKEwCAAAAaHhB1XN11llnaf/+/crLy/Nt++mnn2Sz2ZSamipJGjJkiFauXOn3uBUrVmjo0KFN2tam5i1oUVJmqqC4LMCtAQAAAE4+AQ1XeXl5Sk9PV3p6uiRpx44dSk9P1+7duyVZw/Wuuuoq3/GXX365WrVqpauvvlrff/+91q1bpzvuuEPXXHONIiIiJEm33HKLVqxYoUceeUQ//vijHnnkEa1atUq33nprU7+9JhURaleY3frnPEpRCwAAAKDJBTRcbdq0Sf3791f//v0lSbNmzVL//v117733SpIyMjJ8QUuSoqOjtXLlSmVlZWngwIG64oorNGHCBD399NO+Y4YOHaq33npLCxYsUJ8+fbRw4UItXrxYZ5xxRtO+uSZmGEaFhYSZdwUAAAA0NcM0TTPQjWhucnJy5HQ6lZ2drdjY2EA3p8bOe3KtfvolT29ee4bO6to60M0BAAAAgl5tskFQzblC9eI8RS0YFggAAAA0PcJVC8JaVwAAAEDgEK5aEO+cq2zWugIAAACaHOGqBfGudXU0n2GBAAAAQFMjXLUgTm+1QHquAAAAgCZHuGpB4iKsnqssCloAAAAATY5w1YLEs84VAAAAEDCEqxaEYYEAAABA4BCuWhBvQQuGBQIAAABNj3DVgsRVGBZommaAWwMAAACcXAhXLYi3oEWp21R+cVmAWwMAAACcXAhXLUhEmF2OEOuflLWuAAAAgKZFuGruvnxJevlcaeMrNTrcOzQwm6IWAAAAQJMiXDV3uRnS3i+lgz/W6HDv0MCjFLUAAAAAmlSdwtWePXu0d+9e3/0vv/xSt956q1588cUGaxg8YlOs6+x9NTo8jrWuAAAAgICoU7i6/PLL9cknn0iSMjMzde655+rLL7/UXXfdpT/96U8N2sCTXmw76zqnluGKYYEAAABAk6pTuPrf//6nwYMHS5LefvttnXrqqVq/fr3+/ve/a+HChQ3ZPnh7rnL21+hw31pXFLQAAAAAmlSdwlVJSYkcDockadWqVfr1r38tSerZs6cyMjIarnWQYlOt6/wDUumJA5OTnisAAAAgIOoUrnr37q0XXnhBn332mVauXKmxY8dKkvbv369WrVo1aANPepEJkt0Ksso9cXD1FrRgzhUAAADQtOoUrh555BH99a9/1YgRI3TZZZepb9++kqT333/fN1wQDcQwKgwNPPG8q3hfQQuGBQIAAABNKaQuDxoxYoQOHTqknJwcxcfH+7Zfd911ioyMbLDGwSO2nXR0R43mXVHQAgAAAAiMOvVcFRYWyuVy+YLVrl27NH/+fG3ZskVt27Zt0AZCteq5crLOFQAAABAQdQpXEydO1Ouvvy5JysrK0hlnnKHHH39ckyZN0vPPP9+gDYQkp7cc+4l7ruKjrJ6rbOZcAQAAAE2qTuHqq6++0tlnny1Jevfdd5WYmKhdu3bp9ddf19NPP92gDYRqtdaVr6BFYYlM02zMVgEAAACooE7hqqCgQDExMZKkFStW6KKLLpLNZtOZZ56pXbt2NWgDofJhgdk1CFeeOVdlblO5rtLGbBUAAACACuoUrrp27aply5Zpz549+vjjj3XeeedJkg4cOKDY2NgGbSBUq4WEw0PtCg+1/lkZGggAAAA0nTqFq3vvvVe33367OnbsqMGDB2vIkCGSrF6s/v37N2gDofKFhPN+kcpOHJhY6woAAABoenUqxX7xxRfrV7/6lTIyMnxrXEnSqFGjdOGFFzZY4+AR2Uqyh0llxVJuphTXvtrD4yJDlZlTRMVAAAAAoAnVKVxJUlJSkpKSkrR3714ZhqF27dqxgHBjsdmkmGQpa5dV1KIG4UpirSsAAACgKdVpWKDb7daf/vQnOZ1OpaWlqUOHDoqLi9P9998vt9vd0G2EVLeKgfRcAQAAAE2mTj1Xd999t1555RU9/PDDOuuss2Sapv79739r7ty5Kioq0oMPPtjQ7UQtilp417pizhUAAADQdOoUrl577TW9/PLL+vWvf+3b1rdvX7Vr10433HAD4aox1GIhYScFLQAAAIAmV6dhgUeOHFHPnj0rbe/Zs6eOHDlS70ahCrUYFhjvnXPFsEAAAACgydQpXPXt21fPPPNMpe3PPPOM+vTpU+9GoQp1WEiYghYAAABA06nTsMBHH31U559/vlatWqUhQ4bIMAytX79ee/bs0YcfftjQbYRUqzlXTgpaAAAAAE2uTj1Xw4cP108//aQLL7xQWVlZOnLkiC666CJ99913WrBgQUO3EVL5sMC8TKmstNpDy4cF0nMFAAAANJU6r3OVkpJSqXDF5s2b9dprr+nVV1+td8NwjKi2ki1EcpdKeb+UF7ioQlykp+eKYYEAAABAk6lTzxUCwGaTYrxDA6ufdxVXoaCF2202dssAAAAAiHAVXGJrFq6cEVa4cptSrqv6IYQAAAAAGgbhKpjUsKhFeKhdEaF2SVI2864AAACAJlGrOVcXXXRRtfuzsrLq0xacSC0WEo6PDFVhdpmOFhSrQ6vIRm4YAAAAgFqFK6fTecL9V111Vb0ahGrUYiFhZ2SY9mcXUdQCAAAAaCK1CleUWQ+w2iwkHFFe1AIAAABA42POVTCJrcWwwCjWugIAAACaEuEqmHh7rnIzJHdZtYc6IzxrXRGuAAAAgCZBuAom0YmSYZfMMinvQLWHete6OsqwQAAAAKBJEK6Cic0uxSRbt09Q1CLeE66yKWgBAAAANAnCVbCp4ULCcb5hgfRcAQAAAE2BcBVsariQcPmwQHquAAAAgKZAuAo2zlTr+kQ9V5FWzxXDAgEAAICmQbgKNjXsufLOuTqc52rsFgEAAAAQ4Sr41HAh4SRnuCQpp6hU+a7Sxm4VAAAAcNIjXAWbGi4kHBMeqtjwEEnSvqzCxm4VAAAAcNIjXAUb30LC+yW3u9pD28VHSpL2Hi1o7FYBAAAAJz3CVbCJTpIMm+QulfIPVntou7gISdK+o/RcAQAAAI2NcBVs7CFWwJJOWDEwNd4KV3sZFggAAAA0OsJVMKrhQsL0XAEAAABNh3AVjGpYjt3bc0VBCwAAAKDxEa6Cka9i4Al6ruLpuQIAAACaCuEqGDlrVo7dOyzwQK5LrtKyxm4VAAAAcFIjXAWjGi4knBAVpvBQ6584I6uosVsFAAAAnNQIV8GohsMCDcMoL2rBvCsAAACgURGugpFvIeGMGi8kzLwrAAAAoHERroJRTLIkQyorlgoOV3uot+eKta4AAACAxkW4Ckb2UCk60bpdw4WE6bkCAAAAGldAw9W6des0YcIEpaSkyDAMLVu2rNrjP/30UxmGUeny448/+o5ZuHBhlccUFbWwgg61XUg4q6CxWwQAAACc1EIC+eL5+fnq27evrr76ak2ePLnGj9uyZYtiY2N999u0aeO3PzY2Vlu2bPHbFh4eXr/GNjexKdL+r05cjp2FhAEAAIAmEdBwNW7cOI0bN67Wj2vbtq3i4uKOu98wDCUlJdWjZUGgpgsJe3quMrKKVOY2ZbcZjd0yAAAA4KQUlHOu+vfvr+TkZI0aNUqffPJJpf15eXlKS0tTamqqLrjgAn399dfVPp/L5VJOTo7fpdmr4ULCibHhCrEZKnWb+iWnhQ2NBAAAAJqRoApXycnJevHFF7VkyRK999576tGjh0aNGqV169b5junZs6cWLlyo999/X4sWLVJ4eLjOOussbd269bjPO2/ePDmdTt+lffv2TfF26sfbc3WChYTtNkNJTmtIJEMDAQAAgMZjmKZpBroRkjWUb+nSpZo0aVKtHjdhwgQZhqH333+/yv1ut1sDBgzQsGHD9PTTT1d5jMvlksvl8t3PyclR+/btlZ2d7Te3q1nZtV5aME6K7yTdkl7toVP/ukH/2XFE86f206T+7ZqmfQAAAEALkJOTI6fTWaNsEFQ9V1U588wzq+2VstlsGjRoULXHOBwOxcbG+l2aPV+1wP3SCfJxqnchYXquAAAAgEYT9OHq66+/VnJy8nH3m6ap9PT0ao8JSjGecFXmkgqOVHuot2LgXta6AgAAABpNQKsF5uXladu2bb77O3bsUHp6uhISEtShQwfNnj1b+/bt0+uvvy5Jmj9/vjp27KjevXuruLhYf/vb37RkyRItWbLE9xz33XefzjzzTHXr1k05OTl6+umnlZ6ermeffbbJ31+jCgmTotpK+QesioFRrY57aGoc5dgBAACAxhbQcLVp0yaNHDnSd3/WrFmSpOnTp2vhwoXKyMjQ7t27ffuLi4t1++23a9++fYqIiFDv3r31wQcfaPz48b5jsrKydN111ykzM1NOp1P9+/fXunXrNHjw4KZ7Y00lNqU8XCX3Oe5hvrWujrKQMAAAANBYmk1Bi+akNpPWAmrR5dKWD6TzH5cGXXvcw3YeyteIxz5VeKhNP/xprAyDta4AAACAmjipClqc1CoWtahGcpxVir2oxK0j+cWN3SoAAADgpES4CmY1XEjYEWJX2xiHJOZdAQAAAI2FcBXMvAsJ51S/kLBUcd4V4QoAAABoDISrYOYdFphdg3BFxUAAAACgURGuglktFhJmrSsAAACgcRGugpl3IeHSQqnwaLWHstYVAAAA0LgIV8EsNFyKbG3dPkFRC+ZcAQAAAI2LcBXsfEMDq5931S4uUpK0l4WEAQAAgEZBuAp2NawY6O25yikqVW5RSWO3CgAAADjpEK6CXQ0XEo52hMgZESqJeVcAAABAYyBcBbsaLiQsSanMuwIAAAAaDeEq2NVmIWEqBgIAAACNhnAV7GqzkDA9VwAAAECjIVwFu9gKwwJPtJCwp+dqLz1XAAAAQIMjXAW7mGTruiRfKsqu9lDmXAEAAACNh3AV7MIipYgE6/aJFhL2rHXFnCsAAACg4RGuWoJarnV1MNelopKyxm4VAAAAcFIhXLUEvrWuqg9X8ZGhigi1S5Iysosau1UAAADASYVw1RLUcCFhwzCoGAgAAAA0EsJVS+Csy1pXBY3ZIgAAAOCkQ7hqCSqWYz8Beq4AAACAxkG4aglqs5Cwd60rwhUAAADQoAhXLUEteq68a12xkDAAAADQsAhXLYG356o4VyrKqfZQ35wreq4AAACABkW4agnCoqTwOOv2CXqvUuOthYQzc4pUWuZu5IYBAAAAJw/CVUvhGxq4t9rD2sY4FGo3VOY29UuuqwkaBgAAAJwcCFctRQ3XurLZDCU7GRoIAAAANDTCVUtRw3AlsdYVAAAA0BgIVy2FM9W6rslCwqx1BQAAADQ4wlVLUaeeK8IVAAAA0FAIVy1FbRYSjmchYQAAAKChEa5aitosJEzPFQAAANDgCFcthbfnypUtuXKrPdTbc7U/q1CmaTZ2ywAAAICTAuGqpXDESA6ndTsno9pDk50RMgypqMStw/nFTdA4AAAAoOUjXLUkvqIW1c+7CguxqW2MQxIVAwEAAICGQrhqSWoYriQqBgIAAAANjXDVktSmHHt8pCRp71EWEgYAAAAaAuGqJfFVDKxFzxXDAgEAAIAGQbhqSZy1KMcez7BAAAAAoCERrloSFhIGAAAAAoZw1ZLUYlggCwkDAAAADYtw1ZJ4e66KsqTi/GoP9fZc5RaVKqeopJEbBgAAALR8hKuWJNwphcVYt0+wkHBkWIjiI0MlUdQCAAAAaAiEq5amNmtdxVMxEAAAAGgohKuWhoWEAQAAgIAgXLU0tVrrylpImHAFAAAA1B/hqqXx9VydeK0rhgUCAAAADYdw1dLUYiFh77DAvfRcAQAAAPVGuGppvMMCa7CQcCo9VwAAAECDIVy1NHUoaHEoz6WikrLGbBUAAADQ4hGuWhpvuCo8IpVU3yMVFxmqyDC7JIpaAAAAAPVFuGppwuOk0Cjr9gnmXRmGUV6OnaGBAAAAQL0Qrloaw6hbxUB6rgAAAIB6IVy1RLWYd0VRCwAAAKBhEK5aIhYSBgAAAJoc4aolYiFhAAAAoMkRrlqiOiwkTM8VAAAAUD+Eq5aoFsMCvXOuMnOKVFrmbsxWAQAAAC0a4aol8g4LzD5xuGoT7VCY3aYyt6nMnKJGbhgAAADQchGuWiJvz1XBIamk+sBksxlKjguXxLwrAAAAoD4IVy1RRLwUYgUm5Wac8HDmXQEAAAD1R7hqiQyjwryrWhS1oOcKAAAAqDPCVUtVi4WEfeXY6bkCAAAA6oxw1VLVaiFhwhUAAABQX4SrloqFhAEAAIAmRbhqqWqxkHBqXKQkaW9WodxuszFbBQAAALRYAQ1X69at04QJE5SSkiLDMLRs2bJqj//0009lGEaly48//uh33JIlS9SrVy85HA716tVLS5cubcR30UzVYlhgkjNchiEVl7p1KN/VyA0DAAAAWqaAhqv8/Hz17dtXzzzzTK0et2XLFmVkZPgu3bp18+3bsGGDpk6dqmnTpmnz5s2aNm2apkyZov/85z8N3fzmzRuuDm09Ye9VWIhNSbGsdQUAAADUR0ggX3zcuHEaN25crR/Xtm1bxcXFVblv/vz5OvfcczV79mxJ0uzZs7V27VrNnz9fixYtqk9zg0tibympj5T5jfTuTGn6PyX78f+528VFKCO7SPuyCtW/Q3wTNhQAAABoGYJyzlX//v2VnJysUaNG6ZNPPvHbt2HDBp133nl+28aMGaP169cf9/lcLpdycnL8LkHPZpcuWSiFxUi710ufPFDt4RS1AAAAAOonqMJVcnKyXnzxRS1ZskTvvfeeevTooVGjRmndunW+YzIzM5WYmOj3uMTERGVmZh73eefNmyen0+m7tG/fvtHeQ5Nq1UWa+Bfr9udPSj+tOO6hlGMHAAAA6iegwwJrq0ePHurRo4fv/pAhQ7Rnzx499thjGjZsmG+7YRh+jzNNs9K2imbPnq1Zs2b57ufk5LScgNX7Qmnnv6WNL0lL/0+6/jPJmVrpMHquAAAAgPoJqp6rqpx55pnaunWr735SUlKlXqoDBw5U6s2qyOFwKDY21u/Soox5UEruJxUekd69RiorqXQIPVcAAABA/QR9uPr666+VnJzsuz9kyBCtXLnS75gVK1Zo6NChTd205iPEYc2/csRKe/4jrf5TpUNS6bkCAAAA6iWgwwLz8vK0bds23/0dO3YoPT1dCQkJ6tChg2bPnq19+/bp9ddfl2RVAuzYsaN69+6t4uJi/e1vf9OSJUu0ZMkS33PccsstGjZsmB555BFNnDhR//jHP7Rq1Sp9/vnnTf7+mpWETtLEZ6S3r5LWPy2lnSX1GOvbneLpucp1lSq7sETOiNBAtRQAAAAISgHtudq0aZP69++v/v37S5JmzZql/v37695775UkZWRkaPfu3b7ji4uLdfvtt6tPnz46++yz9fnnn+uDDz7QRRdd5Dtm6NCheuutt7RgwQL16dNHCxcu1OLFi3XGGWc07ZtrjnpNlM643rq97Hopa49vV2RYiBKiwiTRewUAAADUhWGaphnoRjQ3OTk5cjqdys7Obnnzr0qLpVfHSPu/klIHSTM+lEKsUDXhL5/r233ZeumqgTq31/HnqAEAAAAni9pkg6Cfc4VaCgmTLlkghTulvRul1ff5dvmKWhwtCFTrAAAAgKBFuDoZxXeUJj5n3d7wjPTjh5IqlGOnYiAAAABQa4Srk9UpF0hn3mjdXna9dHSXr+dqL3OuAAAAgFojXJ3MRs+V2p0uFWVL716tVKddEj1XAAAAQF0Qrk5mIWHW+lfhcdK+/2rAlvmSqBYIAAAA1AXh6mQX10G68AVJUuv/vaIxto06nF+swuKyADcMAAAACC6EK0g9xklDbpIk/Tn0r2pv/MLQQAAAAKCWCFewjJ4rpQ5WrFGgZ0L/ov2HswLdIgAAACCoEK5gsYdKF7+qPFuM+tq2K/GLByTWlwYAAABqjHCFcnHttTTtXklSj12LpGcGSWv/LB3ZEeCGAQAAAM0f4Qp+8juO0oMll6vYCJMOb5U+eUB6up/0ynnSxpelgiOBbiIAAADQLBGu4KddXIReKrtA17b5uzTxOanzCEmGtOc/0ge3SY91k/5+qfS/96QSil4AAAAAXiGBbgCal3bxEZKkbdk2qf8V1iVnv/S/JdI3b0uZ30g/fWRdwmKkXr+W+kyROp4t2ewBbj0AAAAQOIQr+EmNs8JVZk6RSsrcCrXbpNgUaejN1uXAD1bI+vZdKXu3lP6mdYlJlk67WDptipR0mmQYAX4nAAAAQNNiWCD8tI52KMxuk9uUMrOLKh/Q9hRp9Bzpls3S1R9Jp8+QwuOk3Axp/V+kv54tvXmxdHRXUzcdAAAACCjCFfzYbIZS4sIlSf/8Zr/M45Vjt9mktKHShKek23+Spr4p9Zoo2UKlbauk586UNjwrucuasPUAAABA4BCuUMl5vZMkSY8u36KZr23SgdwqerAqCnFIp1wgTXldumGDlHaWVFIgfXyX9PIoKfPbJmg1AAAAEFiEK1Tyh7E99cfzT1FYiE1rfjygMU+u0/L/ZdTswa27SdP/ZfVoOZzS/q+lvw6XVs2luiAAAABaNMM87rivk1dOTo6cTqeys7MVGxsb6OYEzJbMXN26OF0/ZORIkiYPSNWcX/dSbHhozZ4gN1P68A7ph/et+wmdpQvmS52HN06DAQAAgAZWm2xAuKoC4apccalb81f9pBfW/iy3aa2D9fiUvjqzc6uaP8mPH1hrZOV6er/6Xymde78UmdA4jQYAAAAaCOGqnghXlW3aeUSz3t6s3UcKZBjSb87urFnndld4aA3XtirKllbdJ216xbof1UYa94jU+6LgKNtumlLWLiljs3UpdUldzpE6/sqacwYAAIAWiXBVT4SrquW5SvXAv77XWxv3SJJ6JMboyan91CulFj+jXRukf/5OOvSTdb/bGOn8x6W49o3Q4jpyu6Uj26WMdM/FE6iKsisfGxoldRkpdR8rdTtPikls6tYCAACgERGu6olwVb2V3/+i2e99o0N5xQq1G5p1bg9dN6yz7LYa9kCVuqTPnpA+e1xyl0hh0dKoe6VB10q2GvaENZSyUivoeQNUxmYp8xupOK/ysbZQKbGXlNzX6snaukLK+8X/mJQBVtDqPsY6Lhh65VB7ZaXSwR8lZzspIj7QrQEAAI2IcFVPhKsTO5Tn0uz3vtXK761wMahjvJ6Y0k/tEyJr/iQHfrR6sfb8x7ofkyzFJFkfViMSrDlZftee7RHx1jZHrH94MU3JlSsVZUmFWVLh0epvFxyxglVpFaXmQ8KlpNOsgOS9tDlFCgkrP8btljI3Sz99LP203KqMWFFMstWb1X2M1HmEFBZV858Nmh+3W9q9QfrfEun7f0gFh6zt8R2l5H5SSj/rOrkv8wkBAGhBCFf1RLiqGdM09c6mvbrvn98pv7hMUWF2zZnQW5cMTJVR0x4bt9uah7XqPqk4t3YNsIVYQSssWnLlWMHJrMOixWHRUlIf/yDVurtkD6nd8+RmSltXWkHr50+kkvzyfXaH1Olsqcsoa45WSaHnkm9dF3uuSwqsS3GB//6SAmueWuogKXWgdd22d+3beCLuMunwz1bv3cEtUmQrqU0P6xKTfPL1xJmmtO+/VqD6bpmUu798X2ik9e9Slbi08rDlvSZwobjACuX5hyR3qfV3JzQ80K0CAJwA4aqeCFe1s+dIgW57e7O+3HlEkpQY61D/9vHq3yFO/TvE67R2TkWEnWC4X2GW1YtUcEQqPFL5uvCoVHC0fFtpNWtm2R1SRJwVvMLjqr+d0MUqEW9r4CXfSl3Szs/Le7WydjXs80vWh/uUAeVhK3VQ7eZ8lbqkA99LGd9YYSrjG+mX7/xDYUWOWCt0tulZHrja9JCcHRr+5xdIpmktfP2/JdJ370lZu8v3OZzWgtmnXiR1Gm4NH83YLO1Pt+bn7U+Xju6o+nnjOpSHrYTOnnMw3joPw+Osn2+w/xxN0/pdzd4jFRy2vhCI9QydbInBvNQl5ey33mv+ofLgVHBIyj/suT5YfvvYMB4SIaUNtYrjdDlHantKy/w5Aags76D0xXPW/zM6DLFGuLTuzt+AZopwVU+Eq9orc5t66bPtenLlT3KVuv322W2GeibFWGHLE7o6tY6qee9WVUoKPcHrqPUB1xFbHpxCI+r3ZhqaaVq9QD8tl3Z/Yc0rC42wwlFopBQW6bkfZV2HRfnvD42wLkd3SXs3Wpd9/7V6647l7OAftpL7WD1lRdlS5v/KQ1TmN9acIXdp5ecIjZQSe1sf9AqOWG0/sv34vYIhEdbi0W16Sm084SsmxQoKht16v75rW+X7fvtCKlzsTfs/mYNbpP+9Z4Wqw1vLt4dGST3GSadOlrqOOnF1yMKj1s/YG7Yy0q2f34kYtvLzuKovAsLjpKjWUqtu1s85EHO9Sl1Szj4pe2+Fyx7/+1X15oVEWPPTYlOk2FTr2tnO/3Z4XPP/UJGTYQ1j3vOltPdL69/XXVK757CFWv+O7lIreFUUnWQVyOk80vqgRYEcoOXJOyitf0ra+Erlv5cxKdbvfpeR1hd4/A1oNghX9US4qrvC4jJ9uy9bX+8+qq93Z+mr3Ud1INdV6bi4yFD1a18etvq2j5MzooaLE8MaTnnop/KwtXeT1QulY36d7WFSdKL1AbgqEfGeIZF9pKS+1nWrrpULi5S6rOGCh7ZYIeTgj9LBn6wQUlbcKG9Rkid4HRO47KHltyvus4eVB9GQCGu4le86/Pj7jmy3QtUv/6vwc3NI3c+zAlW3MVYAro/CLCvQesNWzn5rm3ceYHU9sccT1ba897B1Dytwte5hzVusS0gpKbLWosvNsNrnva4Ypo4t4FJd2yJbWeHBOzftREIjrV6u2BTrPOwyyvo2N1DD5spKrB7MvRvLA1VVv0chEVZYimzluW5d/X3vXFHTlA78IG3/RPp5jbTz35XPg8RTy8NW2tDm98VRc1RWYv09aO5B3ZUrHd1Z/qVKeKwUFhP8vdfBoNRljUg4utO6HNnhub1DKsqx/vb3u0Jqd3rDnkd5B6X1T0sbXy4PVSkDrHnZu9ZbX76WHfN5qW1vK2x1HmH9DXBEN1x7UCuEq3oiXDUc0zSVkV2k9D1ZvsD17b7sSr1bktStbbQGdkzQoI7xGtQxQanxEfXr3TrZFOVI+78qD1t7N1rDlbyc7SsEKc91bLv6/c+jrNQa8njwx/LAdfBHq8fLLLPmcPlduytvPzYQBootxPpAf+pkq6cqvAl/90td/mGrYgGWite5mdKhrVLO3uM/l8Pp6UmsELxad7P+Z56TYc0b87v2hKjCIzVra0iE5EytcGnvfz+2nX8gKimyXit7nyes7fW/7R1Wd7zX6niW9e/SdVTjDpnJP2z1Ru35j7TH0zt8bNgxbFavbvszrEvqIKugSUO0qaTIeu2f11iBK2Oz/367Q0obYgWttqdI0W2tEBvVxr/QTkNwl1m/w3m/SPkHrCDQ5hTPFy8B/PDvyisP/Dn7q75deMQK6jHJ1iXWe53ivy06qeF/bscqdVkf2g9v81x+9ly2SXmZVTzAkBwx5WEr3Fl+2+/aab2P1IHWeRAopmn9XSk4Yv0Oe4ftVxzWX3DY+gIuNsX621Dxb0V0YuNUCDZN67W9gemoJzwd2Wld5+xTjf6/07q71Pcyqe+lVvvr6nihasRsqdu55X8/SgqtoknbP7Uux/4NsIVK7QeX92yn9G/4edcNwbsuaMXh8ge+t/6GRbW2/mZFtZGiWlW47f0iynO7Ga4fSriqJ8JV4youdevHzBx9vdsTuPZkadfhykOJkmLDNbBjvAZ3StDAtAT1SIqpebl3WH/gju6wPpC36dl8CyqYpmS6rQ907lJP6Cotv+8utb6Nrnj/2P3uEuuDTEmhVf2x4nVJofUhuaSo/LriNke0dMoEqecFzfdndCxXrtVzefAnT2+i5/rIdutnWVch4f4fRGOTPR+I2pcHqciEhg84JYXlH46zdlvf4m5bXfkDaGyq1NUzP6nziNoPjTRN6/fB92F3m/UzO/hj1UM3w51S6mBPmBostRtgffhtCvmHrA9YP3t6tioWUzlWRLwVtKK9l0TrQ0p0Yvm2qLbWFwj5B6S8A1avYt4vx9z2XBccqvo8cjitn4G3sE67gdYHpIZSlC0d2mb1iB/ZcUxw2i+5qlhrsD6i2lg9vTEp1rkekWD9DoQ4jrkOO+b+Mce4y6zz58h2/3Mra3f1v48RCVZgd+XUfQRAfEfPOeq5NGSho6Ic63fjwA/WJTfDE6KOVghOlUem1JgtxPo74/1SpuIXNE7PkOFSV9VfNJ3o+kQ/z9Ao62eX0Mm6ju8oxXey9n37jlUR1vvlimGzAk2/y6We59e8B7nKUNVfGnGXf6g6nvzD0o61nrD1if/8X8kqxhWZ4BmF4R2ZEe6ZThBeYbtnW8URHOFOz9+JtlJ0m8rVl2uqYpDa/3X52qCFR2v/XBU5Yv2D2PmPW7+rAUS4qifCVdM7nOfSf3cd1aZdR/XljiP6375slbr9T82Y8BCdnmb1ag3qmKA+qU6FhzbxulhAc+Y3fLNC8Dq8zZrLF5tc/kGy0nVy8yo8YZrWt53bVlvhYtd6/w9yhs0atuPt1UoZUP6hsjCrvIfA7/Lz8Qu2SFYvX/tB5T1Trbo1j2FapmmF6Z/XWIVysveUh6Kq5k02CMP64BadaH0oO/BD1cNX4ztaYavdQCtwJZ1W/bfO7jKr/Ye2ei6e8/PQTzUbduqILR8+6u0NqXgd3dYKabkZ5T20uZnlQ11zM6z7jTmcuaKwaKlVF6vXz3fpYhVTiogrP66kyApZRdlWqHF5rytuy7Gui7I9Xwr8UPn1QqOsANx+sBW6UgedOACXFFo//wM/WL9z3jB1vOHkx7KHeZZLaWWdMxWXUYlsZfW45OyrMMR4n/Xv0mjnrkdMSnlwOjZERbWu/m9dUY4VsNL/Lu1eX77d4ZROvdAaNpg6qOrnyD9khaovXzomVM22lmepa4g5usP6smX7p9KOdVaIbCgh4eVB69gvZyreNgxrTvGJgpRvXdB+VhGnpD7We8g/WH4pOFzhvqcYUP7Bqud337G9Yb/IqQPCVT0RrgKvsLhM6XuytHHnEW3ceURf7Tqq/GL/X7gwu02npTo1sGO8+rePV7/2cUpyUtYYaJGKC6yA9fNqK3Ad2uK/P9xphaGjO6uf52XYrFL5FT/otuoanOuTud3WBxtvb1TeAc/tCr1Qvp6qQ9aHlshWx3yIOs7tyNb+PSBlJdYH772brMu+TdYH8mPZw6wPUt6eLdNt9UQd+snTK7Wt+t6O6ERrOFZC5/LeC294iklumOG6pml9sKsYwHIyrPBSWmR9SVHl9XH2mab1ob3i+eS9eD+QNobCLOvfYc9Ga0jr3k1VFzpK6OL5wmCQNY8ve69/kDq64/g9bDHJ1jDUtr08PdetrDUnI1uVB6qwqNq/R3eZdX5m77NCXM6+Y27vtT5o20L8i/rU6NppBYKGmqN4ZLu0+S0pfZGUXaH3qFVXqzerz6VWUZ7GCFXH4y6zfpdcuZ4lXLwjMworj9goKfAfyVFS6P93ozivfm3xC1L9rTDVtlfdhva53VZo9FVd9YSv069unCGktUC4qifCVfNTWubWj5m5+nLHEW3adURf7jiqQ3mV/wedGOtQ31SrQEa/9nE6LdWp2HAKZQAtTvZeqydn22rrm9xjv8WNSbY+/CR09v+wG9+x8efaNEfe+Y72Bvx7WHhU2veVNT/NO8+zJnP37GHWB/7W3TyX7lYwbt3V+mCMunG7rWF8e78sD1xVBeCqRCRYH4jbnlIeptr2DExVUq+y0qavGlsdt1va9bnVm/X9PypU+jOs4jsZ6Y0fqhpDcUEVX9B4L794hg177pcVW0EqpX95r1Rdg1SQIVzVE+Gq+TNNU7sOF2jjziP6766j2rw3W1syc+Su4mzu0ibKF7b6psapZ3KMHCEMJwRaDHeZ9SE/Z581/Cehc9PNjUI579Clvf+1gtb+r60w17qbJzx1twJUXFrAv4U+aRQc8QTfL62Klwe3SHHtKwQoz3VUm+YfApoTV65n2OAiK3B5JfezQlX3MS3z52maLfN91QDhqp4IV8GpoLhU3+3P0eY9WUrfk6XNe7O050jlOQJhdptOSYlVv1Sn2sVHKNoRqpjwkAqXUEU7rNtRYSGyUUQDAABU5cgO6aePrSGhXUeftOGjpSNc1RPhquU4nOfSN3uzfWFr854sHS2o+aKfhiFFh1lBK9oTvGLCQxQbHqq4yFDFRYTKGRmmuAjP/chQOSPCPNehCrU3g8nwAAAAqDPCVT0Rrlou0zS150ih0vdm6Zs9WTqcX6zcohLlFpUqt6hUea5S3/1jqxXWRbQjRE6/4BWq8FC7wkPtcoTYrNshdjlCbQr33LduV7y2jk2MDVfr6DDW/gIAAGhChKt6IlzBNE25St3KKSpRXhXBK7uwRDmFJcoqLFFWgXWdXVDsu59TVKLG+M1yRoSqa9todW0TrW6J0eriud0uLoLhiwAAAI2gNtmgGS7tDASeYRi+Hqa2dZgXX+Y2K4SvYk/4KlF2YYkKS8rkKnGrqLRMRSVlcpW6resSt1ylZSoqcfttL/JsO5TnUnZhif6766j+u8t/XYmIULu6tI1S1zbRVvhqG6OubaOV1iqSoYkAAABNhHAFNAK7zVB8VJjio8IkRTXIcxaVlGn7wXxtO5inbQfytO1ArrYdyNOOQ/kqLCnT//bl6H/7/Nc4CbUbSnKGy5Aht2nKNK1eObcpuT3X1v3ybaqwzxFqU0SoXRFhdkWG2T23QxQRalNkWIgiPNsiw6wgGuk5LjY8VInOcCXFhqttjEMhBDwAAHASIFwBQSI81K5eKbHqleLfHV1a5tbuIwXaesAKXT8fyNPWA3n6+WCeCorLqqyYWFOFJWXKUs0LgFTFMKTW0Q4lO8OVGGsFriS/2w4lxoYr5iRYjyzPVaqMrEKVuk1FO0IU5QhRtCNEYSGETwAAWgLmXFWBOVdoCdxuUxk5RcrIKpRhGLIZks0wZDMMGd7bNsmQta+qY1ylZSosdquguFQFJWUqKi5TQXGZ3+3CkjIVFpdWuF2mowXF+iXHpV9yimpcGCQqzK5EZ7gvhCU7veErQkmx4Up0OtQ6ylGnuWWmaSrXVapDuS4dzi/W4TyXDuUV63BesYpKy3zVH50R3gqQntuRYYoKs9eoiIirtEyZ2UXan1Wk/VmFysgu1P5sz+2sIu3PLlRuUWmVjw2z2xTlsCvaU/7fF7zCQxQdVuG2w+63VIC3eqX3dmSonbl3AAA0MOZcAZDNZqhdXITaxUUErA1ut6lD+S79ku1SZk6RMnOK9Eu25zqnSJnZ1iXXVar8YmvY4/aD+cd9vlC7obYxVs+XtwfMCl7hKil163C+S4fzinUor1iH8ly++4fzilVc5q7TewixGXJGWGHL6S2/HxGqKEeIDuW5lOEJUIfyimv0fLHhIQoLsSvfVarCkjJJUnGZW8UF7lotE1AVw7AqVMb6BbAQRYeHKjbcqlwZGxGq2PBQxUaEeK7L98WEh56wF800TRWWlCmvqFQ5FQq9eAu/5HruF5W45YwIVSvP8NiECpfY8BCqXgIAWiR6rqpAzxXQtPJdpX7BKyO7QvjyXB/Mc9W7AmO0I0Sto8PUKtqhVlHWtSPEppwiT/VHT9ERbwGS2gay8FCbUpwRSomLULIzXMlxEUpxhislLkIpceFKdkYoylH+nVZpmVv5xWXKd5Uq32UFE9/tIs/t4jLluUqV5wsy5VUrG3rpAMkqjlIxeNkNwxeYvK9ZVs/XCvHMSWwVFab4yDAlRIcpIdIKXq2iwxRmt8mUfP/epswKt60d3hZ45xFK1hcK3kI0EaF2hXvmDPq2hdkVHmLzXDdOL59pmipzmyopM1Vc5laJ91Lqf7/MbcoRYrUx3NdGmxwhdtnpfay1ir3T0Y4QxUeFNdtiPgXFpb6/cVFhIUqOC69zrzyCT0FxqXYdLtCuw/naebhAxaVudU+MUe+UWKXGR/DFUzNFKfZ6IlwBzU9JmVsHc11VBq/MnCI5QmxqXSE0tY4Os+5XCFPhofYav55pmioqcXvCVrGyvSX3PWX4c4tK1So6TMlOKzilOCMUFxkakP8xetua6ypfs61ib1JOUYlyikqVU1jiCZKlvkDpXVogz1X1kMXjqaqXrOJC244Qm7ILS3Q0v1hH8ot1OL9YR/OLlV9c1kg/hdoLC7H5QpitDv9upimVmaYnPLlV4rZu1/f/qqF2w7fGnRW4/ANYqN2mEJshu81QiM2mELv3tiG7zabQ49y3GYYVRj3R1FO/pnJYPSbMGpIvsFYsYlN+O0QRYTZPsRtrnyPE1mC/C4XFZfrF09v9S65LBzy/97/kWkOPD+QU6Zccl68n2Cs+MtT3u9/a8zehlfdvQpRDbWKs61bRYYp2NExvar6rVBnZhcrItr4kyvRcZ2QX+m5nF1buoQ6z25TodCjZ6flixln+hUyyZ7h0QlTDr3Podls90fnFpSpweYZ9F1tf6hS4Sv3u2w1DUY4QRTnsigornzMa6bD7hjI35tDk4lK374umXJf19y2/uHyplIpfQLlNU2F2m8JCKlzs1u9S+X27376wEGt/lCNEUWF2RTms87ku7yfPVWqFp0MF2nk43xekdh3O1y85ruM+LsYRop7JMTolOVanJMeqV3KseiTF1Or/XWgchKt6IlwBONmUllkfXCoGr5yiEpW6zfK5XQ4rPFlzw2o2F+1YRSXWnLzDeVboqnj7SEGxjuQVq9Tt7TG05v55X8W67dlm+I6Q5z+5PSGzsNhawqCw2FruoKjErcKS8qUPmlqo3VCo3VbhYgUdV6lbLs9yCyVlLet/xYZh9YKGhdQmCFrH2G2GSt1u37zN481VrEpUmF2FJWWqbedqWIhNMY6Q8rbYy9tS3jb/NnrfS5nb1C+eHveattU7xzTfVaoDuTXrlXeE2Ky5qM5wxUeGyW2aKnPLc+2t+uq57d1umnK7reqvZW5rX0FJqQqLy5TvKqsUSBtCpCeYVAwoVk+06euR9oZ6SRW2eSvaynd8QYWe+7oO7W6I9xMZVjFUlt+PDLPeZ0RYiA7munwh6lDe8QOUJMVFhiqtVZQ6topUiM2mHzNztPWXvCrfo82QOrWO8gtcvVJi1TbG4fsb7F2b0/v3ruIyLkUllZd9cZumZ961IbutfK6190sYm2FVPbbZDNk9+2w2KcRWHkL9r60vVMLsthOGUVdpme/LyqwC/+VqsgqLdbSg/La1v0QrZw1TZFhgZzIRruqJcAUALZPbbfo+dHgDV2Ede9NM0/oA4gtPIVZYCDsmSNUkhJa5Tb917vzWuvOsi+ddC6+kzFSZ2+25NlXqNlVa5lap58Oz3+0K992m1Q/lDazesCrfbfnaWjG8mjJ97SrwFK+xbpcXsSn03C9upPAaEWpXktNa2iHRM9/Se9u6ONQ2JlwRYXaVuU1lFRR7ita4dMhTxOawZy7mobziCvMxXQ3emxoTHuIJQdaw4CRPz1PF+xWro5aUuXUg16WMLKsITkZWoa+3K8NTJOdEH9bryzCkyFC7Ij2hyBseInzhwS632/Qbxuy9nee530Ajk08oItQq/hPjK/Rj9ZpVvB9iM+Qqc6u4tMKlzC1XiXXt3VZ+TJlvf2FxmfKKS+vdA50QFaa0VpHq1CrKClKtI32BKi4yrNLxJWVu/XwwTz9k5OiHjFz9kJGj7/fn6HB+1fN5vXNXA/Wl0fFU7AX0Xofabcp3lSqrsEQFdfh9W/+Hc5QSwPnjEuGq3ghXAADUXmmZW0WlVoXRwmIrCJa63So9Jgh6b5d5hlIeez/EbigxJlxtPcGpoYbtVaWguFSH84pVUFymUrd/W8rbfcx2txVwS8tMGYahxFiHL0BFOxr+G3ZXaZkO5Lg8lUiLlFNUUqGnQf69Dr7eBvlu221WqA6x2XzrFkaFWUP6osJCFB5av2Gc3p6TPJc1vDDPZQ3Zy3eVqqikTP6h3hPgJV8vtLcHuny7FfujHHZFO0IrVE61N8m6id6h1t7hkt734h0ume8dLunyhM3iUiVEhimtdZQ6tYpSh1aRckbUf3kR0zR1MNel7ysGrowcbT+Yd9wwa7cZCq8wlNjhmcsZHmpTuOfa2+NaZpbPEy1zm76hztZtb89nee+o9SWQN6iW+W7XNknYDPkq8lrXVrGouMgwv9veIlK9UmLlCAns0EjCVT0RrgAAANAcFZWUadfhAtlt8gSn8uI4TV3IxTTLC/i4Ssr8egitayuERYWFeIJTmGLCQ4KugAul2AEAAIAWKDzUrh5JMYFuhiSrlzEsxFBYiK1Rem2DUfOsUwoAAAAAQYZwBQAAAAANgHAFAAAAAA2AcAUAAAAADYBwBQAAAAANgHAFAAAAAA2AcAUAAAAADYBwBQAAAAANgHAFAAAAAA2AcAUAAAAADYBwBQAAAAANgHAFAAAAAA2AcAUAAAAADYBwBQAAAAANICTQDWiOTNOUJOXk5AS4JQAAAAACyZsJvBmhOoSrKuTm5kqS2rdvH+CWAAAAAGgOcnNz5XQ6qz3GMGsSwU4ybrdb+/fvV0xMjAzDaJDnzMnJUfv27bVnzx7FxsY2yHPi5MH5g/rg/EFdce6gPjh/UB/N6fwxTVO5ublKSUmRzVb9rCp6rqpgs9mUmpraKM8dGxsb8BMEwYvzB/XB+YO64txBfXD+oD6ay/lzoh4rLwpaAAAAAEADIFwBAAAAQAMgXDURh8OhOXPmyOFwBLopCEKcP6gPzh/UFecO6oPzB/URrOcPBS0AAAAAoAHQcwUAAAAADYBwBQAAAAANgHAFAAAAAA2AcAUAAAAADYBw1USee+45derUSeHh4Tr99NP12WefBbpJaIbWrVunCRMmKCUlRYZhaNmyZX77TdPU3LlzlZKSooiICI0YMULfffddYBqLZmXevHkaNGiQYmJi1LZtW02aNElbtmzxO4bzB8fz/PPPq0+fPr7FOocMGaKPPvrIt59zBzU1b948GYahW2+91beN8wfHM3fuXBmG4XdJSkry7Q/Gc4dw1QQWL16sW2+9VXfffbe+/vprnX322Ro3bpx2794d6KahmcnPz1ffvn31zDPPVLn/0Ucf1RNPPKFnnnlGGzduVFJSks4991zl5uY2cUvR3Kxdu1Y33nijvvjiC61cuVKlpaU677zzlJ+f7zuG8wfHk5qaqocfflibNm3Spk2bdM4552jixIm+DzGcO6iJjRs36sUXX1SfPn38tnP+oDq9e/dWRkaG7/Ltt9/69gXluWOi0Q0ePNi8/vrr/bb17NnT/MMf/hCgFiEYSDKXLl3qu+92u82kpCTz4Ycf9m0rKioynU6n+cILLwSghWjODhw4YEoy165da5om5w9qLz4+3nz55Zc5d1Ajubm5Zrdu3cyVK1eaw4cPN2+55RbTNPnbg+rNmTPH7Nu3b5X7gvXcoeeqkRUXF+u///2vzjvvPL/t5513ntavXx+gViEY7dixQ5mZmX7nksPh0PDhwzmXUEl2drYkKSEhQRLnD2qurKxMb731lvLz8zVkyBDOHdTIjTfeqPPPP1+jR4/22875gxPZunWrUlJS1KlTJ1166aXavn27pOA9d0IC3YCW7tChQyorK1NiYqLf9sTERGVmZgaoVQhG3vOlqnNp165dgWgSminTNDVr1iz96le/0qmnniqJ8wcn9u2332rIkCEqKipSdHS0li5dql69evk+xHDu4Hjeeust/fe//9WmTZsq7eNvD6pzxhln6PXXX1f37t31yy+/6IEHHtDQoUP13XffBe25Q7hqIoZh+N03TbPSNqAmOJdwIjfddJO++eYbff7555X2cf7geHr06KH09HRlZWVpyZIlmj59utauXevbz7mDquzZs0e33HKLVqxYofDw8OMex/mDqowbN853+7TTTtOQIUPUpUsXvfbaazrzzDMlBd+5w7DARta6dWvZ7fZKvVQHDhyolMSB6nir53AuoTo333yz3n//fX3yySdKTU31bef8wYmEhYWpa9euGjhwoObNm6e+ffvqqaee4txBtf773//qwIEDOv300xUSEqKQkBCtXbtWTz/9tEJCQnznCOcPaiIqKkqnnXaatm7dGrR/ewhXjSwsLEynn366Vq5c6bd95cqVGjp0aIBahWDUqVMnJSUl+Z1LxcXFWrt2LecSZJqmbrrpJr333ntas2aNOnXq5Lef8we1ZZqmXC4X5w6qNWrUKH377bdKT0/3XQYOHKgrrrhC6enp6ty5M+cPaszlcumHH35QcnJy0P7tYVhgE5g1a5amTZumgQMHasiQIXrxxRe1e/duXX/99YFuGpqZvLw8bdu2zXd/x44dSk9PV0JCgjp06KBbb71VDz30kLp166Zu3brpoYceUmRkpC6//PIAthrNwY033qi///3v+sc//qGYmBjfN31Op1MRERG+dWc4f1CVu+66S+PGjVP79u2Vm5urt956S59++qmWL1/OuYNqxcTE+OZ2ekVFRalVq1a+7Zw/OJ7bb79dEyZMUIcOHXTgwAE98MADysnJ0fTp04P3b0/A6hSeZJ599lkzLS3NDAsLMwcMGOArjwxU9Mknn5iSKl2mT59umqZVlnTOnDlmUlKS6XA4zGHDhpnffvttYBuNZqGq80aSuWDBAt8xnD84nmuuucb3/6g2bdqYo0aNMlesWOHbz7mD2qhYit00OX9wfFOnTjWTk5PN0NBQMyUlxbzooovM7777zrc/GM8dwzRNM0C5DgAAAABaDOZcAQAAAEADIFwBAAAAQAMgXAEAAABAAyBcAQAAAEADIFwBAAAAQAMgXAEAAABAAyBcAQAAAEADIFwBAAAAQAMgXAEA0MAMw9CyZcsC3QwAQBMjXAEAWpQZM2bIMIxKl7Fjxwa6aQCAFi4k0A0AAKChjR07VgsWLPDb5nA4AtQaAMDJgp4rAECL43A4lJSU5HeJj4+XZA3Ze/755zVu3DhFRESoU6dOeuedd/we/+233+qcc85RRESEWrVqpeuuu055eXl+x7z66qvq3bu3HA6HkpOTddNNN/ntP3TokC688EJFRkaqW7duev/99xv3TQMAAo5wBQA46dxzzz2aPHmyNm/erCuvvFKXXXaZfvjhB0lSQUGBxo4dq/j4eG3cuFHvvPOOVq1a5Reenn/+ed1444267rrr9O233+r9999X165d/V7jvvvu05QpU/TNN99o/PjxuuKKK3TkyJEmfZ8AgKZlmKZpBroRAAA0lBkzZuhvf/ubwsPD/bbfeeeduueee2QYhq6//no9//zzvn1nnnmmBgwYoOeee04vvfSS7rzzTu3Zs0dRUVGSpA8//FATJkzQ/v37lZiYqHbt2unqq6/WAw88UGUbDMPQH//4R91///2SpPz8fMXExOjDDz9k7hcAtGDMuQIAtDgjR470C0+SlJCQ4Ls9ZMgQv31DhgxRenq6JOmHH35Q3759fcFKks466yy53W5t2bJFhmFo//79GjVqVLVt6NOnj+92VFSUYmJidODAgbq+JQBAECBcAQBanKioqErD9E7EMAxJkmmavttVHRMREVGj5wsNDa30WLfbXas2AQCCC3OuAAAnnS+++KLS/Z49e0qSevXqpfT0dOXn5/v2//vf/5bNZlP37t0VExOjjh07avXq1U3aZgBA80fPFQCgxXG5XMrMzPTbFhISotatW0uS3nnnHQ0cOFC/+tWv9Oabb+rLL7/UK6+8Ikm64oorNGfOHE2fPl1z587VwYMHdfPNN2vatGlKTEyUJM2dO1fXX3+92rZtq3Hjxik3N1f//ve/dfPNNzftGwUANCuEKwBAi7N8+XIlJyf7bevRo4d+/PFHSVYlv7feeks33HCDkpKS9Oabb6pXr16SpMjISH388ce65ZZbNGjQIEVGRmry5Ml64oknfM81ffp0FRUV6cknn9Ttt9+u1q1b6+KLL266NwgAaJaoFggAOKkYhqGlS5dq0qRJgW4KAKCFYc4VAAAAADQAwhUAAAAANADmXAEATiqMhgcANBZ6rgAAAACgARCuAAAAAKABEK4AAAAAoAEQrgAAAACgARCuAAAAAKABEK4AAAAAoAEQrgAAAACgARCuAAAAAKAB/H8GF/2cabONyQAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Calculate and print accuracies for training and cross-validation sets\n",
    "model.eval()\n",
    "with torch.no_grad():\n",
    "    # Training set accuracy\n",
    "    tr_correct = 0\n",
    "    tr_total = 0\n",
    "    for images, labels in trLoader:\n",
    "        outputs = model(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        tr_total += labels.size(0)\n",
    "        tr_correct += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    tr_accuracy = 100 * tr_correct / tr_total\n",
    "    \n",
    "    # Test set accuracy\n",
    "    cv_correct = 0\n",
    "    cv_total = 0\n",
    "    for images, labels in cvLoader:\n",
    "        outputs = model(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        cv_total += labels.size(0)\n",
    "        cv_correct += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    cv_accuracy = 100 * cv_correct / cv_total\n",
    "\n",
    "print(f'Accuracy on training set: {tr_accuracy:.2f}%')\n",
    "print(f'Accuracy on cross-validation set: {cv_accuracy:.2f}%')\n",
    "\n",
    "# Plot training and cross-validation losses\n",
    "plt.figure(figsize=(10, 5))\n",
    "plt.plot(range(1, num_epochs+1), train_losses, label='Training Loss')\n",
    "plt.plot(range(1, num_epochs+1), cv_losses, label='Cross-Validation Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.title('Training and Cross-Validation Loss')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
